﻿using System;
using System.Collections.Generic;
using System.Configuration;
using System.Linq;
using Medianamik.Core;
using Medianamik.Core.Configuration;
using Medianamik.Core.Logging;
using Medianamik.Globalization;
using Medianamik.Core.Sugar;

namespace Medianamik.UI.Web.Services
{
    public class ResourceService : IResourceService
    {
        #region Fields

        private static readonly ILogger Logger = new ObjectFactory<ILogger>().Get("Logger");
        private IMedianamikResourceDataManager _resourceDataManager;
        private static readonly MedianamikConfigSection Config = (MedianamikConfigSection)ConfigurationManager.GetSection(ConfigurationSectionName.MEDIANAMIK);

        #endregion

        #region .ctors

        public ResourceService()
            : this(null)
        {

        }

        protected internal ResourceService(IMedianamikResourceDataManager resourceDataManager)
        {
            _resourceDataManager = resourceDataManager ??
                new MedianamikResourceDataManager();
        }

        #endregion

        #region Server

        private static object _lock = new object();
        private static string _mainAssemblyName;
        public static string MainAssemblyName
        {
            private get
            {
                lock (_lock)
                {
                    if (_mainAssemblyName == null)
                        _mainAssemblyName = String.Empty;
                    return _mainAssemblyName;
                }

            }
            set
            {
                lock (_lock)
                    _mainAssemblyName = value;

            }
        }
        public IEnumerable<Resource> GetAllNewOrUpdatedResources(string clientAssemblyName, IEnumerable<string> cultureNames,
            DateTime lastResourcesUpdate)
        {
            return GetNewOrUpdatedResources(clientAssemblyName, cultureNames.Union(new[] { "" }), lastResourcesUpdate,
                GlobalizationData.MedianamikResourceSets);
        }

        public IEnumerable<Resource> GetNewOrUpdatedResources(string clientAssemblyName, IEnumerable<string> cultureNames,
            DateTime lastResourcesUpdate, IEnumerable<string> resourceSets)
        {

            _resourceDataManager.ErrorMessage = "";
            List<Resource> newOrUpdatedResources;
            Logger.Trace(String.Format("GetAllNewOrUpdatedResources - Client assembly : {2} - Culture Names : {0} - Date : {1}",
                cultureNames.ToString(", "), lastResourcesUpdate, clientAssemblyName));

            var errorMsg = "Erreur lors de l'exportation des ressources pour l'assembly client " + clientAssemblyName + " ";

            try
            {
                var maxUpload = Config.ResourceUpdateLimit == 0 ? 100 : 
                    Config.ResourceUpdateLimit > 1000 ? 1000 : Config.ResourceUpdateLimit;
                newOrUpdatedResources = _resourceDataManager
                    .GetNewOrUpdatedResources(cultureNames,
                    lastResourcesUpdate, resourceSets, maxUpload).ToList();
            }
            catch (Exception ex)
            {
                Logger.Error(errorMsg, ex);
                return new List<Resource>();
            }

            if (!String.IsNullOrEmpty(_resourceDataManager.ErrorMessage))
            {
                Logger.Error(errorMsg + _resourceDataManager.ErrorMessage);
                return new List<Resource>();
            }

            /*if (newOrUpdatedResources.Count > Config.ResourceUpdateLimit)
            {
                Logger.Error(errorMsg + "La limite de ressources (" + Config.ResourceUpdateLimit + ") est dépassée.");
                return new List<Resource>();
            }*/

            Logger.Trace("Exportation des ressources terminée avec succès pour l'assembly client " + MainAssemblyName + ". (" + newOrUpdatedResources.Count + ")");
            return newOrUpdatedResources;
        }

        #endregion

        #region Client

        public void UpdateResourcesFromService()
        {
            var sets = _resourceDataManager.GetResourceSets(ResourceListingTypes.InternalResourceOnly);
            UpdateResourceSetsFromService(sets, true);

        }
        public void UpdateResourceSetsFromService(IEnumerable<string> resourceSets, bool updateConfig)
        {
            UpdateResourceSetsFromService(resourceSets, LastResourceUpdate, updateConfig);
        }

        public void UpdateResourceSetsFromService(IEnumerable<string> resourceSets, DateTime lastResourceUpdate, bool updateConfig)
        {
            var newOrUpdatedResources = new List<Resource>();
            Logger.Trace("Récupération des ressources.");

            try
            {
                var cultureNames = GetCultureNames();
                newOrUpdatedResources = GetNewOrUpdatedResourcesFromService(cultureNames, lastResourceUpdate, resourceSets).ToList();

                var exceptionsResources = _resourceDataManager.GetResourcesForLocalUseOnly();
                newOrUpdatedResources = newOrUpdatedResources.Where(
                                        updateResource =>
                                        !exceptionsResources.Any(
                                        exc => exc.Id == updateResource.Id && exc.ResourceSet == updateResource.ResourceSet)).ToList();

                Logger.Trace("Récupération des ressources terminée avec succès. (" +
                  newOrUpdatedResources.Count + ")");

                newOrUpdatedResources.ForEach(r => _resourceDataManager.UpdateOrAdd(r));

                if (updateConfig && newOrUpdatedResources.Count() > 0)
                    SetLastResourcesUpdate(newOrUpdatedResources.Max(updateRes => updateRes.ModifiedOn.GetValueOrDefault()));
            }
            catch (Exception ex)
            {
                Logger.Error("Erreur lors de la récupération des ressources.", ex);
            }
        }
        private static DateTime LastResourceUpdate
        {
            get
            {
                DateTime lastResourcesUpdate = DateTime.MinValue;
                ConfigManager.ReloadConfiguration();
                var configurations = ConfigManager.GetAllConfigurations();

                if (configurations.ContainsKey(MedianamikConfigurations.LastResourcesUpdateKey))
                    DateTime.TryParse(configurations[MedianamikConfigurations.LastResourcesUpdateKey],
                                      out lastResourcesUpdate);

                return lastResourcesUpdate;
            }
        }
        public static void SetLastResourcesUpdate(DateTime lastUpdate)
        {
            ConfigManager.SaveConfiguration(MedianamikConfigurations.LastResourcesUpdateKey,
                                            lastUpdate.ToString());
        }

        private static IEnumerable<Resource> GetNewOrUpdatedResourcesFromService(IEnumerable<string> cultureNames, DateTime lastResourcesUpdate, IEnumerable<string> resourceSets)
        {
            var newOrUpdatedResources = new List<Resource>();

            Service<IResourceService>.Use(resourceService =>
            {
                newOrUpdatedResources =
                    resourceService.GetNewOrUpdatedResources(MainAssemblyName, cultureNames,
                      lastResourcesUpdate, resourceSets).ToList();
            });

            return newOrUpdatedResources;
        }
        private static IEnumerable<string> GetCultureNames()
        {
            return ConfigManager.GetMedianamikCultures().Select(c => c.CultureName);
        }

        #endregion

        protected internal IMedianamikResourceDataManager ResourceDataManager
        {
            get
            {
                return _resourceDataManager;
            }
        }
    }
}
